<?php
/**
 * Created by PhpStorm.
 * User: jyolo
 * Date: 2017/2/8
 * Time: 11:12
 */

namespace app\base\library;
use app\base\model\Menu;
use app\base\model\Models;
use app\base\model\Module;
use app\base\model\Rule;
use ReflectionClass;
use think\Db;


/**
 * 自动创建菜单规则
 * @package bg\base\library
 */

class BuildMenu
{
    protected $rule = [];
    protected $moduel;
    protected $error;
    protected static $_instance;
    protected $unaccept_module_file = ['config','theme']; //排除 module 的文件夹
    protected $unaccept_method = ['__construct','_initialize'];//排序Controller的一些方法
    function __construct()
    {
        return ;
    }
    function __clone()
    {
        return;
    }

    /**
     * 单例初始化
     */
    public static function init(){
        if(!(self::$_instance instanceof self)){
            self::$_instance = new self;
        }
        return self::$_instance;
    }

    /**
     * 创建子菜单
     *
     */
    public function build($menu_data =[]){

        if($menu_data){

            $data = $this->get_rule_data(false,$menu_data);

            if(!$data) {
                $this->error = '获取rule_data失败，请先生成文件';
                return false;
            }
            return $this->build_menu_data($menu_data,$data);

        }

        return false;


    }

    /**
     * 通过模块信息 创建菜单
     */
    public function build_by_module($post){

        $module = new Module();
        
        $module = $module->field('module_name,file_name,models_id')->where(['id'=>$post['id']])->find();


        $models = Models::all($module['models_id']);

        Db::startTrans();
        $menu = new Menu();
        $isset = $menu->where(['module'=>$module['file_name']])->find();

        if($isset) {
            $flag = $menu->where(['module'=>$module['file_name']])->delete();
            if(!$flag){
                $this->error = '清理旧数据失败';
                return false;
            }
            
        }


        //生成顶级菜单
        unset($post['id']);

        $top_menu_data['name'] = $post['module_name'];
        $top_menu_data['module'] = $post['file_name'];
        $top_menu_data['controller'] = $top_menu_data['action'] = $post['file_name'];
        $top_menu_data['icon'] = $post['icon'];

        $flag = $menu->isUpdate(false)->save($top_menu_data);

        if(!$flag){
            Db::rollback();
            $this->error = '创建失败，code:0001';
            return false;
        }

        $son = [];

        foreach($models as $k =>$v){

            $son[$k]['parentid'] = $menu->getLastInsID();
            $son[$k]['name'] = $v['models_name'];
            $son[$k]['module'] = $top_menu_data['module'];
            $son[$k]['controller'] = strtolower(str_replace('_','',$v['table_name']));
            $son[$k]['action'] = 'index';
            $son[$k]['isshow'] = '0';

        }

        //生成子菜单
        $flag = $menu->saveAll($son);

        //创建子菜单的其它的权限菜单，add,edit 等等 暂时未写
        foreach($flag as $k => $v){
            $son[$k]['id'] = $v->id;
        }

        $sub_menu_data = $this->build($son);

        if(!$sub_menu_data){
            $this->error = $this->getError().',code:0003';
            Db::rollback();
            return false;
        }

        Db::commit();
        return true;
    }

    /**
     * 生成菜单数据
     */
    public function build_menu_data($menu_data,$class_info){
        $_return = [];
        $arr = [];
        $menu = new Menu();
        foreach($menu_data as $k => $v){
            $menu_data[$k]['son'] = [];
            if(!$class_info){
                $this->error = '自动获取，没有获取到该控制器下有其它的acion';
                return false;
            }
            foreach($class_info as $subk => $subv){

                if(strtolower($v['module']) == strtolower($subv['module']) && strtolower($v['controller']) == strtolower($subv['controller'])){

                    //排序自己的操作方法
                    if(strtolower($v['action']) == strtolower($subv['action'])) continue;
                    $map['module'] = strtolower($subv['module']);
                    $map['controller'] = strtolower($subv['controller']);
                    $map['action'] = strtolower($subv['action']);
                    $res = $menu->where($map)->find();
                    //如果数据库里面已经有了 直接跳过
                    if($res)continue;

                    $arr['parentid'] = $v['id'];
                    $arr['name'] = $subv['rule_name'];
                    $arr['module'] = strtolower($subv['module']);
                    $arr['controller'] = strtolower($subv['controller']);
                    $arr['action'] = strtolower($subv['action']);
                    $arr['isshow'] = 1; //自动生成的菜单默认隐藏
                    array_push($_return,$arr);
                }

            }
        }

        if(count($_return)){
            $flag = $menu->saveAll($_return);
            if($flag) return $flag;
            $this->error = '数据添加失败';
            return false;
        }else{
            $this->error = '该控制器下的所有子菜单已经存在';
            return false;
        }


    }
    /**
     * 重新载入权限列表
     */
    public function getError(){

        return $this->error;
    }

    /**
     * 获取APP_PATH路径下面的所有的module 以及module里面的所有的控制器
     */
    protected function get_modules(){
        $files = glob(APP_PATH.'*');

        $modules = [];
        foreach($files as $k => $v){
            if(!is_dir($v))continue;
            $arr = explode('/',$v);
            $module_dir = array_pop($arr);
            if(in_array($module_dir,$this->unaccept_module_file))continue;

            $files = glob(APP_PATH.$module_dir.'/'.config('url_controller_layer').'/*.php');
            foreach($files as $f){
                $arr = explode('/',$f);
                $modules[$module_dir][] = str_replace('.php','',array_pop($arr));
            }
        }
        $this->moduel = $modules;

        return $this;
    }

    /**
     *指定module
     */
    protected function set_modules($menu_data){
        $module = [];
        foreach($menu_data as $k => $v){
            $module[$v['module']][] = ucfirst($v['controller']);
        }


        foreach ($module as $k => $v){
            $module[$k] = array_unique($v);
        }

        $this->moduel = $module;

        return $this;
    }


    /**
     * 获取后台所有module下面的的类 以及里面的action 自动生成 权限认证数据
     * @param auto true|false 自动|指定
     */
    protected function get_rule_data($auto = true ,$data = ''){
        if($auto == true){
            $this->get_modules();
        }else{
            $this->set_modules($data);
        }
        

        $namspace = [];


        foreach($this->moduel as $k => $v){

            foreach ($v as $subk =>$f){
                $namspace = config('app_namespace').'\\'.$k.'\\'.config('url_controller_layer').'\\'.$f;

                if(!class_exists($namspace)){
                    $this->error = $namspace.'未找到，请先生成模块';
                    return false;
                }

                $class = new ReflectionClass($namspace);

                $common_class = strtolower(config('app_namespace').'\\base\\'.config('url_controller_layer').'\\Common');

                //剔除掉 没有继承 common的class
                if(strtolower($class->getParentClass()->getName()) != $common_class) continue;

                //剔除掉指定不加入权限的class 比如首页 和 登录
                $rule_title = $this->get_note($class);


                if($rule_title == 'uncheck') continue;
                if(!$rule_title) return false;

                $methods = $class->getMethods();

                foreach ($methods as $mk => $mv){
                    //剔除掉非public 的 function
                    if(!$mv->isPublic()) continue;
                   // 剔除掉 __construct 或者 _initialize
                    if(in_array($mv->getName() ,$this->unaccept_method)) continue;


                    $arr['namespace'] = $namspace;
                    $arr['module'] = $k;
                    $arr['controller'] = strtolower($f);
                    $arr['action'] = $mv->getName();
                    $arr['rule_title'] = $rule_title;
                    $name = $this->get_note($mv);
                    if(!$name) return false;
                    $arr['rule_name'] = $name;
                    if(!in_array($arr ,$this->rule)){
                        array_push($this->rule,$arr);
                    }

                }


            }

        }




        return $this->rule;

    }
    /**
     * 获取注释
     */
    protected function get_note($obj){

        $note = $obj->getDocComment();

        $preg = '/\[menu\]([\s\S]*)\[\/menu\]/';
        preg_match($preg,$note,$match);

        if(isset($match[1])){

            $note = preg_replace(['/\s*/','/\*?/'],'',$match[1]);
            if($note) return $note;
        }
        $this->error = '获取该控制器下注释失败';
        return false;
    }
}